package com.it;

import com.it.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

@WebServlet(urlPatterns = "/*", loadOnStartup = 0,
        initParams = {@WebInitParam(name = "base-package", value = "com.it")})
public class DispatchServlet extends HttpServlet {

    private static final String EMPTY = "";

    /** 扫描的基础包*/
    private String basePackage = EMPTY;

    private List<String> packagesName = new ArrayList<>();

    private Map<String, Object> instanceMap = new HashMap<>();

    private Map<String, String> nameMap = new HashMap<>();

    private Map<String, Method> urlMethodMap = new HashMap<>();

    private Map<Method, String> methodPackageMap = new HashMap<>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        basePackage = config.getInitParameter("base-package");
        try {
            scanBasePackage(basePackage);
            instance(packagesName);
            ioc();
            handlerUrlMethod();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("框架加载失败");
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }


    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();
        String path = uri.replace(contextPath, EMPTY);
        Optional<Method> method = Optional.ofNullable(urlMethodMap.get(path));
        method.ifPresent(m -> {
            String packageName = methodPackageMap.get(m);
            String controllerName = nameMap.get(packageName);
            Object controllerObj = instanceMap.get(controllerName);
            m.setAccessible(true);
            try {
                m.invoke(controllerObj);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }


    private void scanBasePackage(String basePackage) {
        Optional<URL> resource = Optional.ofNullable(this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/")));
        String path = resource.map(URL::getPath).orElse("");
        File basePackageFile = new File(path);
        Optional<File[]> files = Optional.ofNullable(basePackageFile.listFiles());
        files.ifPresent(file -> {
            for (File temp : file) {
                if (temp.isDirectory()) {
                    scanBasePackage(basePackage + "." + temp.getName());
                }
                if (temp.isFile()) {
                    packagesName.add(basePackage + "." + temp.getName().split("\\.")[0]);
                }
            }
        });

    }

    private void instance(List<String> packagesNames) {
        if (packagesNames.isEmpty()) {
            return;
        }
        packagesNames.forEach(packageName -> {
            try {
                Class clazz = Class.forName(packageName);
                String subName = clazz.getName().substring(clazz.getName().lastIndexOf(".")).replaceAll("\\.", EMPTY);
                String clazzName = subName.substring(0, 1).toLowerCase() + subName.substring(1);
                if (clazz.isAnnotationPresent(Controller.class)) {
                    Controller controller = (Controller) clazz.getAnnotation(Controller.class);
                    String controllerName = controller.value();
                    if (EMPTY.equals(controllerName)) {
                        controllerName = clazzName;
                    }
                    instanceMap.put(controllerName, clazz.newInstance());
                    nameMap.put(packageName, controllerName);
                }
                if (clazz.isAnnotationPresent(Service.class)) {
                    Service service = (Service) clazz.getAnnotation(Service.class);
                    String serviceName = service.value();
                    if (EMPTY.equals(serviceName)) {
                        serviceName = clazzName;
                    }
                    instanceMap.put(serviceName, clazz.newInstance());
                    nameMap.put(packageName, serviceName);
                }
                if (clazz.isAnnotationPresent(Repository.class)) {
                    Repository repository = (Repository) clazz.getAnnotation(Repository.class);
                    String respositoryName = repository.value();
                    if (EMPTY.equals(respositoryName)) {
                        respositoryName = clazzName;
                    }
                    instanceMap.put(respositoryName, clazz.newInstance());
                    nameMap.put(packageName, respositoryName);
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("类加载失败");
            }
        });
    }

    private void ioc() throws IllegalAccessException {
        for (Map.Entry<String, Object> instance : instanceMap.entrySet()) {
            Field[] declaredFields = instance.getValue().getClass().getDeclaredFields();
            for (Field declaredField : declaredFields) {
                if (declaredField.isAnnotationPresent(Autowired.class)) {
                    String value = declaredField.getAnnotation(Autowired.class).value();
                    declaredField.setAccessible(true);
                    declaredField.set(instance.getValue(), instanceMap.get(value));
                }
            }
        }
    }

    private void handlerUrlMethod() {
        if (packagesName.isEmpty()) {
            return;
        }
        packagesName.forEach(packageName -> {
            try {
                Class clazz = Class.forName(packageName);
                if (clazz.isAnnotationPresent(Controller.class)) {
                    Method[] methods = clazz.getMethods();
                    StringBuffer baseUrl = new StringBuffer();
                    if (clazz.isAnnotationPresent(RequestMapping.class)) {
                        RequestMapping controllerClazzMapping = (RequestMapping) clazz.getAnnotation(RequestMapping.class);
                        baseUrl.append(controllerClazzMapping.value());
                    }
                    for (Method method : methods) {
                        if (method.isAnnotationPresent(RequestMapping.class)) {
                            RequestMapping methodMapping = method.getAnnotation(RequestMapping.class);
                            baseUrl.append(methodMapping.value());
                            urlMethodMap.put(baseUrl.toString(), method);
                            methodPackageMap.put(method, packageName);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("获取映射失败");
            }
        });
    }


}
